#include "libkynetinfo.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <linux/route.h>
static void parse_port_state(int *st, int port, FILE *fp)
{
    char line[1024] = "\0";
    int lnr = 0;
    unsigned long rxq, txq, time_len, retr, inode;
    int num, local_port, rem_port, d, state, uid, timer_run, timeout;
    char rem_addr[128] = "0", local_addr[128] = "0";
    struct sockaddr_storage localsas, remsas;
    struct sockaddr_in *localaddr = (struct sockaddr_in *)&localsas;
    struct sockaddr_in *remaddr = (struct sockaddr_in *)&remsas;

#if HAVE_AFINET6
    char addr6[INET6_ADDRSTRLEN];
    struct in6_addr in6;
    extern struct aftype inet6_aftype;
#endif

    while (fgets(line, 1024, fp))
    {
        if (lnr++ == 0)
            continue;
        num = sscanf(line,
                     "%d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %X %lX:%lX %X:%lX %lX %d %d %lu %*s\n",
                     &d, local_addr, &local_port, rem_addr, &rem_port, &state,
                     &txq, &rxq, &timer_run, &time_len, &retr, &uid, &timeout, &inode);
        if (local_port == port)
        {
            *st = state;
            break;
        }
    }
    if (strlen(local_addr) > 8)
    {
#if HAVE_AFINET6
        /* Demangle what the kernel gives us */
        sscanf(local_addr, "%08X%08X%08X%08X",
               &in6.s6_addr32[0], &in6.s6_addr32[1],
                &in6.s6_addr32[2], &in6.s6_addr32[3]);
        inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
        inet6_aftype.input(1, addr6, &localsas);
        sscanf(rem_addr, "%08X%08X%08X%08X",
               &in6.s6_addr32[0], &in6.s6_addr32[1],
                &in6.s6_addr32[2], &in6.s6_addr32[3]);
        inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
        inet6_aftype.input(1, addr6, &remsas);
        localsas.ss_family = AF_INET6;
        remsas.ss_family = AF_INET6;
#endif
    }
    else
    {
        sscanf(local_addr, "%X", &localaddr->sin_addr.s_addr);
        sscanf(rem_addr, "%X", &remaddr->sin_addr.s_addr);
        localsas.ss_family = AF_INET;
        remsas.ss_family = AF_INET;
    }
}

int kdk_net_get_port_stat(int port)
{
    FILE *fp = fopen("/proc/net/tcp", "r");
    if (!fp)
        return -1;
    int state = 0;
    parse_port_state(&state, port, fp);

    fp = fopen("/proc/net/tcp6", "r");
    if (!fp)
        return -1;
    parse_port_state(&state, port, fp);

    return state;
}

int kdk_net_get_multiple_port_stat(int start, int end, int *result)
{
    for (int i = start; i <= end; i++)
    {
        result[i - start] = kdk_net_get_port_stat(i);
        if (-1 == result[i - start])
            return -1;
    }
    return 0;
}

prouteMapList kdk_net_get_route()
{
    prouteMapList list = NULL, currnode = NULL, prenode = NULL;
    FILE *fp;
    char devname[64];
    unsigned long d, g, m;
    int r;
    int flgs, ref, use, metric, mtu, win, ir;

    fp = fopen("/proc/net/route", "r");

    /* Skip the first line. */
    r = fscanf(fp, "%*[^\n]\n");

    if (r < 0)
    {
        /* Empty line, read error, or EOF. Yes, if routing table
         * is completely empty, /proc/net/route has no header.
         */
        goto ERROR;
    }

    while (1)
    {
        r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
                   devname, &d, &g, &flgs, &ref, &use, &metric, &m,
                   &mtu, &win, &ir);

        if (r != 11)
        {
            if ((r < 0) && feof(fp))
            {
                /* EOF with no (nonspace) chars read. */
                break;
            }
        }

        /*
         * # cat /proc/net/route
         * Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT
         * eth0    00000000        013CA8C0        0003    0       0       0       00000000        0       0       0
         * eth0    003CA8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0
         * 默认网关的特性: dst为0，netmask为0, Gateway不为0
         */
        if ((flgs & (RTF_GATEWAY | RTF_UP)) &&
                g != 0 && d == 0 && m == 0)
        {
            currnode = (prouteMapList)calloc(1, sizeof(routeMapList));
            if (!currnode)
            {
                kdk_net_free_route(list);
                goto ERROR;
            }

            struct sockaddr_in sin;
            memset(&sin, 0, sizeof(struct sockaddr_in));
            memcpy(&sin.sin_addr, &g, 4);
            strcpy(currnode->name, devname);
            strcpy(currnode->addr, inet_ntoa(sin.sin_addr));

            if (!list)
                prenode = list = currnode;
            else
                prenode->next = currnode;
            prenode = prenode->next;
        }
    }

ERROR:
    fclose(fp);
    return list;
}

void kdk_net_free_route(prouteMapList list)
{
    prouteMapList tmp = NULL;
    while (list)
    {
        tmp = list;
        list = list->next;
        free(tmp);
    }
}

pChain kdk_net_get_iptable_rules()
{
    pChain result = NULL, tmp = NULL;
    char line[512] = "\0";
    FILE *fp = popen("iptables -L -n", "r");
    if (NULL == fp)
    {
        pclose(fp);
        return NULL;
    }
    while (fgets(line, 512, fp))
    {
        if (strstr(line, "Chain"))
        {
            // 申请Chain内存
            if (!result)
            {
                result = (pChain)calloc(1, sizeof(Chain));
                tmp = result;
            }
            else
            {
                tmp->next = (pChain)calloc(1, sizeof(Chain));
                tmp = tmp->next;
            }
            if (NULL == tmp)
            {
                kdk_net_free_chain(result);
                result = NULL;
                break;
            }
            int count = sscanf(line, "%*s %s (%[^)]", tmp->total, tmp->policy);
            continue;
        }

        if(!tmp)
            continue;

        if (strstr(line, "target     prot opt source               destination"))
            continue;
        char target[8] = "0", prot[4 + 1] = "0", opt[4 + 1] = "0", source[16] = "0", destination[16] = "0", option[32] = "0";
        pChainData tmpData;
        int count = sscanf(line, "%s %s %s %s %s %[^\n]", target, prot, opt, source, destination, option);
        if (count < 5)
        {
            continue;
        }
        // Chain下每条规则申请一个chainData结构体
        if (!tmp->data)
        {
            tmp->data = (pChainData)calloc(1, sizeof(chainData));
            tmpData = tmp->data;
        }
        else
        {
            tmpData->next = (pChainData)calloc(1, sizeof(chainData));
            tmpData = tmpData->next;
        }
        if (NULL == tmpData)
        {
            kdk_net_free_chain(result);
            result = NULL;
            break;
        }
        if (target)
            strcpy(tmpData->target, target);
        if (prot)
            strcpy(tmpData->prot, prot);
        if (opt)
            strcpy(tmpData->opt, opt);
        if (source)
            strcpy(tmpData->source, source);
        if (destination)
            strcpy(tmpData->destination, destination);
        if (option)
            strcpy(tmpData->option, option);
    }
    pclose(fp);
    return result;
}

void kdk_net_free_chain(pChain list)
{
    pChain tmpChain = NULL;
    pChainData tmpData = NULL;
    while (list)
    {
        tmpChain = list;
        list = list->next;
        while (tmpChain->data)
        {
            tmpData = tmpChain->data;
            tmpChain->data = tmpData->next;
            free(tmpData);
        }

        free(tmpChain);
    }
}
